#include "tokenizer.h"

/* Keywords map */
Keyword keywords[] = {
    {TOKEN_SELECT,  "select"},
    {TOKEN_INSERT,  "insert"},
    {TOKEN_DELETE,  "delete"},
    {TOKEN_UPDATE,  "update"},
    {TOKEN_FROM,    "from"  },
    {TOKEN_WHERE,   "where" },
    {TOKEN_INTO,    "into"  },
    {TOKEN_VALUES,  "values"},
    {TOKEN_SET,     "set"   },
    {TOKEN_JOIN,    "join"  }
};

/* Operators map */
Operator operators[] = {
    {TOKEN_EQUAL,  "==" },
    {TOKEN_GE,     ">=" },
    {TOKEN_LE,     "<=" },
    {TOKEN_NE,     "!=" },
    {TOKEN_ASSIGN,  "=" },
    {TOKEN_GREATER, ">" },
    {TOKEN_LESS,    "<" },
    {TOKEN_LP,      "(" },
    {TOKEN_RP,      ")" },
    {TOKEN_AND,    "and"},
    {TOKEN_OR,     "or" }
};

Token getNextToken(const char *sql, int *index, int *last_index)
{
    Token token = {TOKEN_INVALID, ""};
    if (last_index != NULL) *last_index = *index;

    /* skip space */
    while (sql[*index] != '\0' && isspace(sql[*index]))
        (*index)++;

    /* end of sql */
    if (sql[*index] == '\0') {
        token.type = TOKEN_EOF;
        return token;
    }

    /* match keywords */
    for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
        if (strncmp(&sql[*index], keywords[i].value, strlen(keywords[i].value)) == 0 &&
                    sql[*index + strlen(keywords[i].value)] == ' ') {
            token.type = keywords[i].type;
            strncpy(token.value, keywords[i].value, strlen(keywords[i].value));
            token.value[strlen(keywords[i].value)] = '\0';
            (*index) += strlen(keywords[i].value);
            return token;
        } 
    }

    /* match operator */
    for (size_t i = 0; i < sizeof(operators) / sizeof(operators[0]); i++) {
        if (strncmp(&sql[*index], operators[i].value, strlen(operators[i].value)) == 0) {
            token.type = operators[i].type;
            strncpy(token.value, operators[i].value, strlen(operators[i].value));
            token.value[strlen(operators[i].value)] = '\0';
            (*index) += strlen(operators[i].value);
            return token;
        } 
    }

    /* match identifier */
    if (isalpha(sql[*index])) {
        int i = 0;
        while (isalnum(sql[*index]) || sql[*index] == '_' || sql[*index] == '.') {
            token.value[i++] = sql[*index];
            (*index)++;
        }
        token.value[i] = '\0';
        token.type = TOKEN_IDENTIFIER;
        return token;
    } 

    /* match star */
    if (sql[*index] == '*') {
        (*index)++;
        token.type = TOKEN_STAR;
        token.value[0] = '*';
        token.value[1] = '\0';
        return token;
    }

    /* match number */
    if (isdigit(sql[*index])) {
        int i = 0;
        while (isdigit(sql[*index])) {
            token.value[i++] = sql[*index];
            (*index)++;
        }
        token.value[i] = '\0';
        token.type = TOKEN_NUMBER;
        return token;
    }

    /* match string */
    if (sql[*index] == '\'') {
        (*index)++;
        int str_len = 0;
        while (sql[*index] != '\'' && sql[*index] != '\0')
            token.value[str_len++] = sql[(*index)++];

        if (sql[*index] != '\'') {   /* not match '" */
            errMsg("ERROR: miss the matched \'\n");
            (*index)++;
            token.value[str_len] = '\0';
            token.type = TOKEN_INVALID;
            return token;
        }

        (*index)++;
        token.value[str_len] = '\0';
        token.type = TOKEN_STRING;
        return token;
    }

    /* match comma */
    if (sql[*index] == ',') {
        token.type = TOKEN_COMMA;
        token.value[0] = ',';
        token.value[1] = '\0';
        (*index)++;
        return token;
    }

    /* match miss */
    token.type = TOKEN_INVALID;
    errMsg("ERROR: invalid token %c at index %d\n", sql[*index], *index);
    (*index)++;
    return token;
}
